package org.seedframework.sharding.springglue;


import lombok.extern.slf4j.Slf4j;
import org.seedframework.sharding.resource.*;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.FileCopyUtils;
import java.io.InputStream;
import java.util.Map;
import java.util.Set;


@Slf4j
@org.springframework.context.annotation.Configuration
public class ShardingAutoConfiguration implements InitializingBean, ApplicationContextAware {


    private final ResourceLoader resourceLoader;

    private static final String SCATTER_CONFIG_FILE = "classpath:scattered.toml";

    private static final String HIKARI_CONFIG_FILE = "classpath:hikari.properties";

    private ApplicationContext applicationContext;


    public ShardingAutoConfiguration(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        Resource resource = resourceLoader.getResource(SCATTER_CONFIG_FILE);
        Assert.isTrue(resource.exists(), "sharding configuration file can't find.");
        DataSourceProperties properties = new DataSourceProperties(resource.getInputStream());
        resource = resourceLoader.getResource(HIKARI_CONFIG_FILE);
        String hikariConfig = null;
        if (!resource.exists()) {
            log.info("Hikari-CP is not found config-file by path: classpath:hikari.properties, and using default.");
        } else {
            InputStream inputStream = resource.getInputStream();
            byte[] fileData = FileCopyUtils.copyToByteArray(inputStream);
            hikariConfig = new String(fileData);
        }

        if (!CollectionUtils.isEmpty(properties.getScatters())) {
            String finalHikariConfig = hikariConfig;
            properties.getScatters().forEach(o -> {
                ShardingDataSource shardingDataSource = null;
                shardingDataSource = new ShardingDataSourceBuilder(
                        finalHikariConfig,
                        properties.getDriverClassName(),
                        o.getUrl(),
                        o.getUsername(),
                        o.getPassword()).build();
                shardingDataSource.setId(o.getId());
                ShardingDataSourcePoolContext.addDataSource(o.getId(), shardingDataSource);
            });
        }

        //  initialize double-write data source
        if (!CollectionUtils.isEmpty(properties.getDoubleWrites())) {
            String finalHikariConfig = hikariConfig;

            Map<String, Set<DataSourceProperties.DatabaseConnection>> map = properties.getDoubleWrites();
            for (Map.Entry<String, Set<DataSourceProperties.DatabaseConnection>> entry : map.entrySet()) {
                Set<DataSourceProperties.DatabaseConnection> set = entry.getValue();
                if (set.isEmpty()) {
                    continue;
                }
                set.forEach(o -> {
                    ShardingDataSource shardingDataSource = null;
                    shardingDataSource = new ShardingDataSourceBuilder(
                            finalHikariConfig,
                            properties.getDriverClassName(),
                            o.getUrl(),
                            o.getUsername(),
                            o.getPassword()).build();
                    shardingDataSource.setId("double-writes." + entry.getKey() + "." + o.getId());
                    DoubleWriteDataSourcePoolContext.setDataSourceMap(entry.getKey(), shardingDataSource);
                });
            }

        }

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
